package com.edol.painting.data;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.widget.Toast;

import com.doodle.common.drawable.MeasuredAsyncDrawable;
import com.doodle.common.utils.BitmapPool;
import com.doodle.common.utils.BitmapUtils;
import com.doodle.common.utils.DownloadUtils;
import com.doodle.common.utils.Entry;
import com.doodle.common.utils.Entry.Table;
import com.doodle.common.utils.EntrySchema;
import com.doodle.common.utils.Future;
import com.doodle.common.utils.FutureListener;
import com.doodle.common.utils.JobLimiter;
import com.doodle.common.utils.SDCardHelper;
import com.doodle.common.utils.ThreadPool;
import com.doodle.common.utils.ThreadPool.Job;
import com.doodle.common.utils.ThreadPool.JobContext;
import com.doodle.common.utils.Utils;
import com.edol.painting.PainterConfig;
import com.edol.painting.R;
import com.edol.painting.painting.Theme;
import com.edol.painting.utils.Constant;
import com.edol.painting.utils.PainterUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class PaintingDatabase {

    private static final String TABLE_NAME = PaintingEntry.SCHEMA.getTableName();

    private static final String TAG = "PaintingDatabase";

    private static PaintingDatabase sInstance = null;

    private final SQLiteDatabase mDatabase;

    private List<PaintingChangedListener> mListeners = new ArrayList<PaintingChangedListener>();

    private JobLimiter mJobLimiter;

    private long mSavingId = -1;
    private boolean mToastWhenSaved = false;
    private Object mSaveLock = new Object();

    private Object mCheckLock = new Object();
    private boolean mIsChecking = false;

    private Handler mHandler;

    public interface PaintingChangedListener {
        void onPaintingListChanged();
    }

    public static synchronized void createInstance(Context context) {
        if (sInstance == null) {
            sInstance = new PaintingDatabase(context);
        }
    }

    public static synchronized PaintingDatabase getInstance() {
        return sInstance;
    }

    public PaintingDatabase(Context context) {
        mDatabase = new DatabaseHelper(context).getWritableDatabase();
        mJobLimiter = new JobLimiter(ThreadPool.getInstance(), 1);
        mHandler = new Handler();
    }

    public List<PaintingEntry> getAllPaintings() {
        String sort = PaintingEntry.Columns.LAST_MODIFY + " DESC," + PaintingEntry.Columns.ID;
        return PaintingEntry.SCHEMA.queryObjects(mDatabase, PaintingEntry.class, null, null, null, null, sort);
    }

    public PaintingEntry getPaintingById(long id) {
        PaintingEntry painting = new PaintingEntry();
        if (PaintingEntry.SCHEMA.queryWithId(mDatabase, id, painting)) {
            return painting;
        } else {
            return null;
        }
    }

    public boolean isSaving(long id) {
        if (mSavingId == id) {
            mToastWhenSaved = true;
            return true;
        }
        return false;
    }

    public boolean isSaving() {
        return mSavingId != -1;
    }

    public void waitForSavingDone() {
        if (mSavingId != -1) {
            synchronized (mSaveLock) {
                try {
                    mSaveLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void waitForSavingDone(long id) {
        if (mSavingId == id) {
            synchronized (mSaveLock) {
                try {
                    mSaveLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // Begin : add by liuqiang 2014-5-9 for optimization save new paint
    public interface OnSavePaintingListener {
        public void setOnSavePaintingFinished(boolean b);
    }

    public void asyncSavePainting(final Context context, final PaintingEntry painting, final boolean layer, final OnSavePaintingListener listener) {
        mJobLimiter.submit(new Job<Boolean>() {
            @Override
            public Boolean run(JobContext jc) {
                waitForSavingDone(painting.mId);
                synchronized (mSaveLock) {
                    mSavingId = painting.mId;
                }
                boolean result = painting.saveThumbnail(context);
                Log.d(TAG, "save Thumbnail result:" + result);
                synchronized (mSaveLock) {
                    mSavingId = -1;
                    mSaveLock.notifyAll();
                }
                return result;
            }
        }, new FutureListener<Boolean>() {
            @Override
            public void onFutureDone(Future<Boolean> future) {
                boolean success = future == null ? false : future.get();
                if (success) {
                    PaintingEntry.SCHEMA.insertOrReplace(mDatabase, painting);
                    notifyChange();
                }
                if (success) {
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            savePaintingPage(context, painting, layer, listener);
                        }
                    }, 90);
                } else {
                    new File(painting.mDirectory).delete();
                    listener.setOnSavePaintingFinished(false);
                }
            }
        });
    }

    public void savePaintingPage(final Context context, final PaintingEntry painting, final boolean layer, final OnSavePaintingListener listener) {
        mJobLimiter.submit(new Job<Boolean>() {
            @Override
            public Boolean run(JobContext jc) {
                waitForSavingDone(painting.mId);
                synchronized (mSaveLock) {
                    mSavingId = painting.mId;
                }
                boolean result = painting.savePaintingPage(context);
                Log.d(TAG, "save painting pager result:" + result);
                boolean result2 = false;
                if (layer) {
                    result2 = painting.savePaintingBrushBitmap(context);
                    Log.d(TAG, "save painting brush bitmap result:" + result);
                }
                synchronized (mSaveLock) {
                    mSavingId = -1;
                    mSaveLock.notifyAll();
                }
                boolean saveSuccess = layer ? result && result2 : result;
                listener.setOnSavePaintingFinished(saveSuccess);
                if (mToastWhenSaved) {
                    mToastWhenSaved = false;
                    if (saveSuccess) {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(context,
                                        R.string.painting_saved,
                                        Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                }
                return saveSuccess;
            }
        }, null);
    }
    // End : add by liuqiang 2014-5-9 for optimization save new paint

    public void savePaintingAs(final Context context, final PaintingEntry painting, final boolean layer, final FutureListener<PaintingEntry> listener) {
        mJobLimiter.submit(new Job<PaintingEntry>() {
            @Override
            public PaintingEntry run(JobContext jc) {
                waitForSavingDone(painting.mId);
                synchronized (mSaveLock) {
                    mSavingId = painting.mId;
                }
                PaintingEntry entry = painting.saveAs(context, painting.mPainting, layer ? painting.mPaintingBrush : null);
                synchronized (mSaveLock) {
                    mSavingId = -1;
                    mSaveLock.notifyAll();
                }
                return entry;
            }
        }, new FutureListener<PaintingEntry>() {
            @Override
            public void onFutureDone(Future<PaintingEntry> future) {
                PaintingEntry paint = future == null ? null : future.get();
                if (paint != null) {
                    PaintingEntry.SCHEMA.insertOrReplace(mDatabase, paint);
                    notifyChange();
                }
                if (listener != null) {
                    listener.onFutureDone(future);
                }
            }
        });
    }
    // End   : add by liuqiang 2014-6-11 for save as

    public void savePainting(final Context context, final PaintingEntry painting, final FutureListener<Boolean> listener) {
        mJobLimiter.submit(new Job<Boolean>() {
            @Override
            public Boolean run(JobContext jc) {
                waitForSavingDone(painting.mId);
                synchronized (mSaveLock) {
                    mSavingId = painting.mId;
                }
                boolean result = painting.save(context);
                synchronized (mSaveLock) {
                    mSavingId = -1;
                    mSaveLock.notifyAll();
                }
                if (mToastWhenSaved) {
                    mToastWhenSaved = false;
                    if (result) {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(context, R.string.painting_saved, Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                }
                return result;
            }
        }, new FutureListener<Boolean>() {
            @Override
            public void onFutureDone(Future<Boolean> future) {
                boolean success = future == null ? false : future.get();
                if (success) {
                    PaintingEntry.SCHEMA.insertOrReplace(mDatabase, painting);
                    notifyChange();
                }
                if (listener != null) {
                    listener.onFutureDone(future);
                }
            }
        });
    }

    public void copyPainting(final Context context, final PaintingEntry painting, final FutureListener<PaintingEntry> listener) {
        mJobLimiter.submit(new Job<PaintingEntry>() {
            @Override
            public PaintingEntry run(JobContext jc) {
                waitForSavingDone(painting.mId);
                return painting.copy(jc, context);
            }
        }, new FutureListener<PaintingEntry>() {
            @Override
            public void onFutureDone(Future<PaintingEntry> future) {
                PaintingEntry painting = future == null ? null : future.get();
                if (painting != null) {
                    PaintingEntry.SCHEMA.insertOrReplace(mDatabase, painting);
                    notifyChange();
                }
                if (listener != null) {
                    listener.onFutureDone(future);
                }
            }
        });
    }

    public void exportPainting(final Context context, final PaintingEntry painting) {
        mJobLimiter.submit(new Job<Void>() {
            @Override
            public Void run(JobContext jc) {
                waitForSavingDone(painting.mId);
                final String filePath = painting.export(jc, context);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (filePath != null) {
                            MediaScannerConnection.scanFile(context, new String[]{filePath}, null, null);
                            Toast.makeText(context, context.getResources().getString(R.string.export_success, Constant.PAINTER_EXPORT_DIR), Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(context, context.getResources().getString(R.string.export_failed), Toast.LENGTH_SHORT).show();
                        }
                    }
                });
                return null;
            }
        }, null);
    }

    public void exportAllPainting(final Context context, final List<PaintingEntry> paintings, FutureListener<Void> listener) {
        mJobLimiter.submit(new Job<Void>() {
            @Override
            public Void run(JobContext jc) {
                int size = paintings.size();
                final String[] filePaths = new String[size];
                for (int i = 0; i < size; i++) {
                    PaintingEntry painting = paintings.get(i);
                    waitForSavingDone(painting.mId);
                    filePaths[i] = painting.export(jc, context);
                }

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (filePaths.length > 0) {
                            MediaScannerConnection.scanFile(context, filePaths, null, null);
                            Toast.makeText(context, context.getResources().getString(R.string.export_success), Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(context, context.getResources().getString(R.string.export_failed), Toast.LENGTH_SHORT).show();
                        }
                    }
                });
                return null;
            }
        }, listener);
    }

    public void sharePainting(final Activity a, final PaintingEntry painting) {
        mJobLimiter.submit(new Job<Void>() {
            @Override
            public Void run(JobContext jc) {
                waitForSavingDone(painting.mId);
                if (painting != null) {
                    sharePainting(a, painting.mDirectory);
                }
                return null;
            }
        }, null);
    }

    public void sharePainting(final Activity a, final String path) {
        if (!TextUtils.isEmpty(path)) {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_SEND);
            intent.setType("image/*");
            intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(path)));
            intent = Intent.createChooser(intent, a.getString(R.string.share));
            intent.putExtra(Constant.EXTRA_REQUEST_RESULT_FROM_CHOOSERACTIVITY, true);
            a.startActivityForResult(intent, Constant.REQUEST_SHARE);
        }
    }

    public void sharePaintingWithNfc(final Context context, final PaintingEntry painting) {
        mJobLimiter.submit(new Job<Void>() {
            @Override
            public Void run(JobContext jc) {
                waitForSavingDone(painting.mId);
                if (painting != null && !TextUtils.isEmpty(painting.mDirectory)) {
                    Intent intent = new Intent();
                    intent.setPackage("com.android.bluetooth");
                    intent.setType("image/*");
                    try {
                        Class<?> c = Class.forName("android.content.IntentExt");
                        intent.setAction((String) c.getField("MZ_ACTION_HANDOVER_SEND").get(null));
                    } catch (Exception e) {
                        e.printStackTrace();
                        return null;
                    }
                    Uri uri = Uri.fromFile(new File(painting.mDirectory));
                    intent.putExtra(Intent.EXTRA_STREAM, uri);
                    context.sendBroadcast(intent);
                }
                return null;
            }
        }, null);
    }

    public void sharePaintings(final Activity a, final List<PaintingEntry> paintings, final SparseBooleanArray checked) {
        mJobLimiter.submit(new Job<Void>() {
            @Override
            public Void run(JobContext jc) {
                waitForSavingDone();
                if (paintings != null && paintings.size() > 0 && checked.size() > 0) {
                    ArrayList<Uri> uriList = new ArrayList<Uri>();
                    for (int i = 0, j = checked.size(); i < j; ++i) {
                        if (checked.valueAt(i)) {
                            String dir = paintings.get(checked.keyAt(i)).mDirectory;
                            if (!TextUtils.isEmpty(dir)) {
                                uriList.add(Uri.fromFile(new File(dir)));
                            }
                        }
                    }
                    int size = uriList.size();
                    if (size > 0) {
                        Intent intent = new Intent();
                        intent.setAction(size == 1 ? Intent.ACTION_SEND : Intent.ACTION_SEND_MULTIPLE);
                        intent.setType("image/*");
                        if (size == 1) {
                            intent.putExtra(Intent.EXTRA_STREAM, uriList.get(0));
                        } else {
                            intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList);
                        }
                        intent = Intent.createChooser(intent, a.getString(R.string.share));
                        intent.putExtra(Constant.EXTRA_REQUEST_RESULT_FROM_CHOOSERACTIVITY, true);
                        a.startActivityForResult(intent, Constant.REQUEST_SHARE);
                    }
                }
                return null;
            }
        }, null);
    }

    public static String makeWhereIdsIn(String columnName, long[] ids) {
        if (ids == null) {
            return null;
        }
        int count = ids.length;
        if (count <= 0) {
            return null;
        }

        StringBuilder where = new StringBuilder();
        where.append(columnName + " IN (");
        int last = count - 1;
        for (int i = 0; i < count; ++i) {
            where.append(ids[i]);
            if (i < last) {
                where.append(',');
            }
        }
        where.append(')');

        return where.toString();
    }

    public void deleteByIds(final long[] ids, FutureListener<Void> future) {
        deleteByIds(ids, true, future);
    }

    public void deleteByIds(final long[] ids, final boolean deleteFile, FutureListener<Void> future) {
        if (ids == null || ids.length <= 0) {
            return;
        }

        mJobLimiter.submit(new Job<Void>() {
            @Override
            public Void run(JobContext jc) {
                if (ids.length == 1) {
                    waitForSavingDone(ids[0]);
                } else {
                    waitForSavingDone();
                }
                String whereIdsIn = makeWhereIdsIn(PaintingEntry.Columns.ID, ids);
                if (deleteFile) {
                    List<PaintingEntry> paintings = PaintingEntry.SCHEMA.queryObjects(mDatabase, PaintingEntry.class, whereIdsIn, null, null, null, null);
                    if (paintings != null) {
                        for (PaintingEntry painting : paintings) {
                            try {
                                deleteFile(new File(painting.mDirectory));
                                String str = painting.mDirectory.substring(0, painting.mDirectory.length() - 4);
                                deleteFile(new File(str + "-2.png"));
                                deleteFile(new File(painting.mThumbnail));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        mDatabase.delete(TABLE_NAME, whereIdsIn, null);
                        notifyChange();
                    }
                } else {
                    mDatabase.delete(TABLE_NAME, whereIdsIn, null);
                    notifyChange();
                }
                return null;
            }
        }, future);
    }

    private void deleteFile(File file) {
        if (file.exists()) {
            file.delete();
        }
    }

    public void checkPaintingRemoved() {
        synchronized (mCheckLock) {
            if (!mIsChecking) {
                mIsChecking = true;
                mJobLimiter.submit(new Job<Void>() {
                    @Override
                    public Void run(JobContext jc) {
                        waitForSavingDone();
                        if (SDCardHelper.getInstance().isMounted()) {
                            List<PaintingEntry> paintings = getAllPaintings();
                            if (paintings != null) {
                                int thumbWidth = Constant.PAINTING_THUMB_WIDTH;
                                int thumbHeight = Constant.PAINTING_THUMB_HEIGHT;
                                int width = Constant.PAINTING_WIDTH;
                                int height = Constant.PAINTING_HEIGHT;

                                ArrayList<Long> ids = new ArrayList<Long>();
                                for (PaintingEntry painting : paintings) {
                                    File paintingFile = new File(painting.mDirectory);
                                    if (!paintingFile.exists()) {
                                        ids.add(painting.mId);
                                        new File(painting.mThumbnail).delete();
                                    } else {
                                        Bitmap ThumbBm = confirmThumb(painting.mThumbnail, painting.mDirectory, thumbWidth, thumbHeight, width, height);
                                        if (ThumbBm != null) {
                                            BitmapPool.getPool(MeasuredAsyncDrawable.DRAWABLE_TYPE_THUMB).recycle(ThumbBm);
                                        }
                                    }
                                }
                                int size = ids.size();
                                if (size > 0) {
                                    long[] array = new long[size];
                                    for (int i = 0; i < size; ++i) {
                                        array[i] = ids.get(i);
                                    }
                                    deleteByIds(array, false, null);
                                }
                            }
                        }
                        synchronized (mCheckLock) {
                            mIsChecking = false;
                        }
                        return null;
                    }
                }, null);
            }
        }
    }

    public static Bitmap confirmThumb(String thumbPath, String paintingPath, int thumbWidth, int thumbHeight, int width, int height) {
        Bitmap thumb = null;
        File thumbFile = new File(thumbPath);
        if (!thumbFile.exists()) {
            Bitmap bitmap = BitmapUtils.loadBitmapFromFile(paintingPath, width, height,
                    BitmapUtils.FIT_LENGTH_LARGGER, MeasuredAsyncDrawable.DRAWABLE_TYPE_IMAGE, 0);
            if (bitmap != null) {
                OutputStream os = null;
                try {
                    try {
                        thumb = BitmapUtils.getScaledBitmap(bitmap, thumbWidth, thumbHeight, 0,
                                ScaleToFit.FILL, MeasuredAsyncDrawable.DRAWABLE_TYPE_THUMB, true);
                    } catch (Exception e) {
                        e.printStackTrace();
                        thumb = null;
                    }
                    if (thumb != null) {
                        os = new FileOutputStream(thumbFile);
                        thumb.compress(CompressFormat.JPEG, 100, os);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    Utils.closeSilently(os);
                }
            }
        }
        return thumb;
    }

    public void removePaintingChangeListener(PaintingChangedListener listener) {
        synchronized (mListeners) {
            if (mListeners.contains(listener)) {
                mListeners.remove(listener);
            }
        }
    }

    public void addPaintingChangeListener(PaintingChangedListener listener) {
        if (listener != null) {
            synchronized (mListeners) {
                if (!mListeners.contains(listener)) {
                    mListeners.add(listener);
                }
            }
        }
    }

    public void notifyChange() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                synchronized (mListeners) {
                    for (PaintingChangedListener listener : mListeners) {
                        listener.onPaintingListChanged();
                    }
                }
            }
        });
    }

    @Table("paintings")
    public static class PaintingEntry extends Entry {

        public static final EntrySchema SCHEMA = new EntrySchema(PaintingEntry.class);

        public interface Columns extends Entry.Columns {
            public static final String DIRECTORY = "directory";
            public static final String LAST_MODIFY = "last_modify";
            public static final String THUMBNAIL = "thumbnail";
            public static final String THEME = "theme";
            public static final String LAYER = "layer";
        }

        @Column(value = Columns.DIRECTORY, unique = true)
        public String mDirectory;

        @Column(Columns.LAST_MODIFY)
        public long mLastModify;

        @Column(Columns.THUMBNAIL)
        public String mThumbnail;

        @Column(Columns.THEME)
        public int mTheme = Theme.THEME_DEFAULT;

        @Column(Columns.LAYER)
        public boolean mLayer = true;

        public Bitmap mPaper;
        public Bitmap mPainting;
        public Bitmap mPaintingBrush;

        @Override
        public String toString() {
            return new StringBuilder().append("directory: ").append(mDirectory)
                    .append(", last_modify: ").append(mLastModify)
                    .append(", thumbnail: ").append(mThumbnail)
                    .append(", theme: ").append(mTheme)
                    .append(", layer: ").append(mLayer).toString();
        }

        public synchronized boolean savePaintingBrushBitmap(Context context) {
            if (mPaintingBrush == null) {
                return false;
            }

            long currentTime = System.currentTimeMillis();
            File file = null;
            if (TextUtils.isEmpty(mDirectory)) {
                file = PainterUtils.createNewPaintingFile(context, currentTime, false);
                if (file == null) {
                    return false;
                }
                mDirectory = file.toString();
            } else {
                String str = mDirectory.substring(0, mDirectory.length() - 4);
                file = new File(str + "-2.png");
            }
            mLastModify = currentTime;

            File tmpFile = new File(file.toString() + ".tmp");
            OutputStream os = null;
            boolean success = false;
            try {
                os = new FileOutputStream(tmpFile);
                success = mPaintingBrush.compress(CompressFormat.PNG, 100, os);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Utils.closeSilently(os);
                if (success) {
                    file.delete();
                    tmpFile.renameTo(file);
                } else {
                    Log.e("PaintingDatabase", "savePainting()-save painting failed: " + tmpFile);
                    tmpFile.delete();
                    return false;
                }
            }
            return true;
        }

        public synchronized boolean savePaintingPage(Context context) {
            if (mPainting == null) {
                return false;
            }

            long currentTime = System.currentTimeMillis();
            File file = null;
            if (TextUtils.isEmpty(mDirectory)) {
                file = PainterUtils.createNewPaintingFile(context, currentTime, false);
                if (file == null) {
                    return false;
                }
                mDirectory = file.toString();
            } else {
                file = new File(mDirectory);
            }
            mLastModify = currentTime;

            File tmpFile = new File(file.toString() + ".tmp");
            OutputStream os = null;
            boolean success = false;
            try {
                os = new FileOutputStream(tmpFile);
                success = mPainting.compress(CompressFormat.PNG, 100, os);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Utils.closeSilently(os);
                if (success) {
                    file.delete();
                    tmpFile.renameTo(file);
                } else {
                    Log.e("PaintingDatabase", "savePainting()-save painting failed: " + tmpFile);
                    tmpFile.delete();
                    return false;
                }
            }
            return true;
        }

        public synchronized boolean saveThumbnail(Context context) {
            if (mPainting == null) {
                return false;
            }

            long currentTime = System.currentTimeMillis();
            File file = null;
            if (TextUtils.isEmpty(mDirectory)) {
                file = PainterUtils.createNewPaintingFile(context, currentTime, false);
                if (file == null) {
                    return false;
                }
                mDirectory = file.toString();
                // create a empty mDirectory file
                try {
                    new File(mDirectory).createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            mLastModify = currentTime;

            if (TextUtils.isEmpty(mThumbnail)) {
                mThumbnail = mDirectory + Constant.THUMB_FILE_EXTENSION;
            }

            File thumFile = new File(mThumbnail);
            File tmpFile = new File(thumFile.toString() + ".tmp");

            OutputStream os = null;
            boolean success = false;
            try {
                int width = Constant.PAINTING_THUMB_WIDTH;
                int height = Constant.PAINTING_THUMB_HEIGHT;
                Bitmap thumb = null;
                try {
                    thumb = BitmapUtils.getScaledBitmap(mPainting, width, height, 0, ScaleToFit.FILL, MeasuredAsyncDrawable.DRAWABLE_TYPE_THUMB, false);
                } catch (Exception e) {
                    e.printStackTrace();
                    return false;
                }
                os = new FileOutputStream(tmpFile);
                success = thumb.compress(CompressFormat.JPEG, 70, os);
                //success = mPainting.compress(CompressFormat.JPEG, 70, os);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Utils.closeSilently(os);
                if (success) {
                    thumFile.delete();
                    tmpFile.renameTo(thumFile);
                } else {
                    Log.e("PaintingDatabase", "saveThumbnail() save thumb failed: " + tmpFile);
                    tmpFile.delete();
                    return false;
                }
            }
            return true;
        }

        public synchronized boolean save(Context context) {
            if (mPainting == null) {
                return false;
            }

            long currentTime = System.currentTimeMillis();
            File file = null;
            if (TextUtils.isEmpty(mDirectory)) {
                file = PainterUtils.createNewPaintingFile(context, currentTime, false);
                if (file == null) {
                    return false;
                }
                mDirectory = file.toString();
            } else {
                file = new File(mDirectory);
            }
            mLastModify = currentTime;

            File tmpFile = new File(file.toString() + ".tmp");
            OutputStream os = null;
            boolean success = false;
            try {
                os = new FileOutputStream(tmpFile);
                success = mPainting.compress(CompressFormat.PNG, 100, os);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Utils.closeSilently(os);
                if (success) {
                    file.delete();
                    tmpFile.renameTo(file);
                } else {
                    Log.e("PaintingDatabase", "save painting failed: " + tmpFile);
                    tmpFile.delete();
                    return false;
                }
            }

            if (TextUtils.isEmpty(mThumbnail)) {
                mThumbnail = mDirectory + Constant.THUMB_FILE_EXTENSION;
            }
            file = new File(mThumbnail);

            tmpFile = new File(file.toString() + ".tmp");
            os = null;
            success = false;
            try {
                int width = Constant.PAINTING_THUMB_WIDTH;
                int height = Constant.PAINTING_THUMB_HEIGHT;

                Bitmap thumb = null;
                try {
                    thumb = BitmapUtils.getScaledBitmap(mPainting, width, height, 0, ScaleToFit.FILL, MeasuredAsyncDrawable.DRAWABLE_TYPE_THUMB, false);
                } catch (Exception e) {
                    e.printStackTrace();
                    return false;
                }
                os = new FileOutputStream(tmpFile);
                success = thumb.compress(CompressFormat.JPEG, 100, os);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Utils.closeSilently(os);
                if (success) {
                    file.delete();
                    tmpFile.renameTo(file);
                } else {
                    Log.e("PaintingDatabase", "save thumb failed: " + tmpFile);
                    tmpFile.delete();
                    return false;
                }
            }

            return true;
        }

        public synchronized PaintingEntry saveAs(Context context, Bitmap bitmap, Bitmap bitmapBrush) {
            PaintingEntry painting = new PaintingEntry();

            long currentTime = System.currentTimeMillis();
            File file = PainterUtils.createNewPaintingFile(context, currentTime, false);
            if (file == null || bitmap == null) {
                return null;
            }

            mLastModify = currentTime;

            painting.mDirectory = file.toString();
            painting.mLastModify = mLastModify + 1;
            painting.mThumbnail = painting.mDirectory + Constant.THUMB_FILE_EXTENSION;
            painting.mTheme = mTheme;
            painting.mLayer = bitmapBrush == null ? false : true;

            // save painting page file
            File tmpFile = new File(file.toString() + ".tmp");
            OutputStream os = null;
            boolean success = false;
            try {
                os = new FileOutputStream(tmpFile);
                success = bitmap.compress(CompressFormat.PNG, 100, os);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Utils.closeSilently(os);
                if (success) {
                    file.delete();
                    tmpFile.renameTo(file);
                } else {
                    tmpFile.delete();
                    return null;
                }
            }

            if (bitmapBrush != null) {
                String str = painting.mDirectory.substring(0, painting.mDirectory.length() - 4);
                file = new File(str + "-2.png");
                tmpFile = new File(file.toString() + ".tmp");
                os = null;
                success = false;
                try {
                    os = new FileOutputStream(tmpFile);
                    success = bitmapBrush.compress(CompressFormat.PNG, 100, os);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    Utils.closeSilently(os);
                    if (success) {
                        file.delete();
                        tmpFile.renameTo(file);
                    } else {
                        tmpFile.delete();
                        bitmapBrush.recycle();
                        bitmapBrush = null;
                        return null;
                    }
                }
            }

            // product and save thumb file
            file = new File(painting.mThumbnail);
            tmpFile = new File(file.toString() + ".tmp");
            os = null;
            success = false;
            try {
                int width = Constant.PAINTING_THUMB_WIDTH;
                int height = Constant.PAINTING_THUMB_HEIGHT;
                Bitmap thumb = BitmapUtils.getScaledBitmap(bitmap, width, height, 0, ScaleToFit.FILL, MeasuredAsyncDrawable.DRAWABLE_TYPE_THUMB, false);
                os = new FileOutputStream(tmpFile);
                success = thumb.compress(CompressFormat.JPEG, 100, os);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Utils.closeSilently(os);
                if (success) {
                    file.delete();
                    tmpFile.renameTo(file);
                } else {
                    tmpFile.delete();
                    return null;
                }
            }
            return painting;
        }

        public synchronized PaintingEntry copy(JobContext jc, Context context) {
            PaintingEntry painting = new PaintingEntry();

            long currentTime = System.currentTimeMillis();
            File file = PainterUtils.createNewPaintingFile(context, currentTime, false);
            if (file == null) {
                return null;
            }

            painting.mDirectory = file.toString();
            painting.mLastModify = mLastModify + 1;
            painting.mThumbnail = painting.mDirectory + Constant.THUMB_FILE_EXTENSION;
            painting.mTheme = mTheme;

            if (!Utils.copyFile(jc, mThumbnail, painting.mThumbnail)) {
                return null;
            }

            if (!Utils.copyFile(jc, mDirectory, painting.mDirectory)) {
                return null;
            }

            return painting;
        }

        public synchronized String export(JobContext jc, Context context) {
            long currentTime = System.currentTimeMillis();
            File file = PainterUtils.createNewPaintingFile(context, currentTime, true);
            if (file == null) {
                return null;
            }
            if (PaintingDatabase.getInstance().checkInitBitmap(new File(mDirectory).getName())) {
                BitmapFactory.Options op = new BitmapFactory.Options();
                op.inJustDecodeBounds = true;
                BitmapFactory.decodeFile(mDirectory, op);
                int width = op.outWidth;
                int height = op.outHeight;
                op.inJustDecodeBounds = false;
                if (width != Constant.PAINTING_WIDTH || height != Constant.PAINTING_HEIGHT) {
                    Bitmap bitmap = BitmapFactory.decodeFile(mDirectory, op);
                    if (mPainting != null) {
                        mPainting.recycle();
                        mPainting = null;
                    }
                    mPainting = Bitmap.createBitmap(Constant.PAINTING_WIDTH, Constant.PAINTING_HEIGHT, Config.ARGB_8888);
                    Canvas canvas = new Canvas(mPainting);
                    canvas.drawBitmap(bitmap, null, new Rect(0, 0, Constant.PAINTING_WIDTH, Constant.PAINTING_HEIGHT), null);
                    bitmap.recycle();
                    bitmap = null;
                    savePaintingPage(context);
                    saveThumbnail(context);
                }
            }
            if (Utils.copyFile(jc, new File(mDirectory), file)) {
                return file.getAbsolutePath();
            }
            return null;
        }
    }

    public static final int[] sSamplePaintingResId = new int[]{
            R.drawable.sample_painting_01,
            R.drawable.sample_painting_02,
            R.drawable.sample_painting_03,
        /*R.drawable.sample_painting_04,
        R.drawable.sample_painting_05,
		R.drawable.sample_painting_06,*/
    };

    public static final int[] sSampleThumbResId = new int[]{
            R.drawable.sample_painting_01,
            R.drawable.sample_painting_02,
            R.drawable.sample_painting_03,
         /*R.drawable.sample_painting_04,
		R.drawable.sample_painting_05,
		R.drawable.sample_painting_06,*/
    };

    public static final int[] sSampleThemeId = new int[]{
            Theme.THEME_PAPER_BACK_4,
            Theme.THEME_PAPER_BACK_15,
            Theme.THEME_PAPER_BACK_2,
		/*Theme.THEME_PAPER_BACK_8,
		0xFF066997,
		Theme.THEME_PAPER_BACK_7,
		Color.BLACK*/
    };

    private static boolean sSampleGenerated = false;

    private static final String SAMPLE_FILE_PATH_FORMAT = "SamplePainting%d" + Constant.PAINTING_FILE_EXTENSION;

    public void generateSamplePainting(Context context) {
        if (!sSampleGenerated) {
            SharedPreferences preferences = context.getSharedPreferences(PainterConfig.PACKAGE_NAME, 0);
            sSampleGenerated = preferences.getBoolean(Constant.PREF_SAMPLE_GENERATED, false);
        }
        if (sSampleGenerated) {
            return;
        }

        sSampleGenerated = true;
        SharedPreferences preferences = context.getSharedPreferences(PainterConfig.PACKAGE_NAME, 0);
        preferences.edit().putBoolean(Constant.PREF_SAMPLE_GENERATED, true).commit();

        Resources res = context.getResources();

        String saveFilePath = PainterUtils.getSaveFilePath(context);

        for (int i = sSamplePaintingResId.length - 1; i >= 0; --i) {
            String fileName = String.format(SAMPLE_FILE_PATH_FORMAT, i + 1);
            File paintingFile = new File(saveFilePath, fileName);
            if (paintingFile.exists()) {
                paintingFile.delete();
            }

            PaintingEntry entry = new PaintingEntry();
            entry.mDirectory = paintingFile.getAbsolutePath();
            entry.mLastModify = System.currentTimeMillis();
            entry.mThumbnail = entry.mDirectory + Constant.THUMB_FILE_EXTENSION;
            entry.mTheme = sSampleThemeId[i];
            entry.mLayer = false;

            boolean success = false;
            InputStream input = null;
            OutputStream output = null;
            try {
                input = res.openRawResource(sSamplePaintingResId[i]);
                output = new FileOutputStream(paintingFile);
                success = DownloadUtils.dump(ThreadPool.JOB_CONTEXT_STUB, input, output);
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                Utils.closeSilently(input);
                Utils.closeSilently(output);
            }

            if (!success) {
                paintingFile.delete();
            } else {
                success = false;
                input = null;
                output = null;
                File thumbFile = new File(entry.mThumbnail);
                if (thumbFile.exists()) {
                    thumbFile.delete();
                }
                try {
                    input = res.openRawResource(sSampleThumbResId[i]);
                    output = new FileOutputStream(thumbFile);
                    success = DownloadUtils.dump(ThreadPool.JOB_CONTEXT_STUB, input, output);
                } catch (Throwable t) {
                    t.printStackTrace();
                } finally {
                    Utils.closeSilently(input);
                    Utils.closeSilently(output);
                }
                if (!success) {
                    thumbFile.delete();
                    paintingFile.delete();
                } else {
                    PaintingEntry.SCHEMA.insertOrReplace(mDatabase, entry);
                }
            }
        }
    }

    public static boolean checkInitBitmap(String str) {
        for (int i = sSamplePaintingResId.length; i > 0; --i) {
            String s = "SamplePainting" + i + Constant.PAINTING_FILE_EXTENSION;
            if (str.equals(s)) {
                return true;
            }
        }
        return false;
    }

    private static final class DatabaseHelper extends SQLiteOpenHelper {

        public static final String DATABASE_NAME = "paintings.db";
        public static final int DATABASE_VERSION = 2;

        public DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            PaintingEntry.SCHEMA.createTables(db);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            if (oldVersion < 2) {
                db.execSQL("ALTER TABLE paintings ADD LAYER;");
            } else {
                PaintingEntry.SCHEMA.dropTables(db);
                onCreate(db);
            }
        }
    }
}
